<?php

	/**
	 * @package		CoreProject
	 * @subpackage	Mime
	 *
	 * cpr_Mime_Utils
	 *
	 */

	class cpr_Mime_Utils
	{
		/**
		 * @param	string	$str
		 * @param	string	$fromCharset
		 * @param	string	$toCharset
		 * @param	bool	$withSpecialParameters = false
		 * @return	string
		 */
		public static function DecodeHeaderString($str, $fromCharset, $toCharset, $withSpecialParameters = false)
		{
			$bool = false;

			$old_str = $str;
			$str = ($withSpecialParameters) ? cpr_Mime_Utils::WordExtensionsDecode($str) : $str;
			$bool = ($old_str != $str);
			unset ($old_str);

			$str = preg_replace('/\s+/',' ', $str);
			$str = str_replace('?= =?', '?==?', $str);

			$encodeArray = cpr_Mime_Utils::SearchEncodedPlaces($str);

			$c = count($encodeArray);
			for ($i = 0; $i < $c; $i++)
			{
				$tempArr = cpr_Mime_Utils::QBDecodeString($encodeArray[$i], $fromCharset, $toCharset);
				if (strlen($tempArr[0]) > 0)
				{
					$str = str_replace($encodeArray[$i], cpr_Core_Utils::ConvertEncoding($tempArr[1], $tempArr[0], $toCharset), $str);
				}
				$bool = true;
			}

			$str = preg_replace('/[;]([a-zA-Z])/', '; $1', $str);
			return ($bool) ? $str : cpr_Core_Utils::ConvertEncoding($str, $fromCharset, $toCharset);
		}

		public static function EncodeHeaderString($str, $fromCharset, $toCharset, $changeCharset = true)
		{
			$out = '';
			if ($changeCharset)
			{
				$str = cpr_Core_Utils::ConvertEncoding($str, $fromCharset, $toCharset);
			}

			$array = cpr_Mime_Utils::QBEncodeString($str, $toCharset);
			for ($i = 0, $c = count($array); $i < $c; $i++)
			{
				$out .= ($c > 1) ? $array[$i].CRLF.' ' : $array[$i];
			}
			return trim($out);
		}

		public static function QBEncodeString($str, $toCharset)
		{
			$outarray = array();
			$outarray = (strtolower($toCharset) == 'utf-8') ?
					cpr_Mime_Utils::SmartChunk($str, CPR_MIME_DEFAULT_QB, $toCharset, true) :
					cpr_Mime_Utils::SmartChunk($str, CPR_MIME_DEFAULT_QB, $toCharset, false);

			if (!cpr_Core_Utils::IsLatin($str))
			{
				for ($i = 0, $c = count($outarray); $i < $c; $i++)
				{
					if (CPR_MIME_DEFAULT_QB == CPR_MIME_QUOTED_PRINTABLE_SHORT)
					{
						$outarray[$i] = '=?'.strtolower($toCharset).'?Q?'.str_replace('?', '=3F', str_replace(' ','_', str_replace('_', '=5F', cpr_Mime_Utils::QuotedPrintableWithLineBreak($outarray[$i], true)))).'?=';
					}
					elseif (CPR_MIME_DEFAULT_QB == CPR_MIME_BASE64_SHORT)
					{
						$outarray[$i] = '=?'.strtolower($toCharset).'?B?'.base64_encode($outarray[$i]).'?=';
					}
				}
			}
			return $outarray;
		}


		/**
		 * @param	string	$str
		 * @return	array	array[0] - charset, array[1] - string
		 */
		function QBDecodeString($str)
		{
			$out = array('', $str);
			if (substr(trim($str), 0, 2) == '=?')
			{
				$pos = strpos($str, '?', 2);
				$out[0] = substr($str, 2, $pos - 2);
				$encType = strtoupper($str{$pos+1});
				switch ($encType)
				{
					case 'Q':
						$str = str_replace('_', ' ', $str);
						$out[1] = quoted_printable_decode(substr($str, $pos + 3, strlen($str) - $pos - 5));
						break;
					case 'B':
						$out[1] = base64_decode(substr($str, $pos + 3, strlen($str) - $pos - 5));
						break;
				}
			}
			
			return $out;
		}

		/**
		 * @param	string	$str
		 * @param	string	$transferEncoding
		 * @param	string	$toCharset
		 * @param	bool	$isUtf8 = false
		 * @return	array
		 */
		function SmartChunk($str, $transferEncoding, $toCharset, $isUtf8 = false)
		{
			$outArray = array();
			if ($isUtf8)
			{
				$outArray = cpr_Core_Utils::Utf8ChunkSplit($str, (int) round((CPR_MIME_LINE_LENGTH_LIMIT - strlen($toCharset) - 7) / 3));
			}
			else
			{
				$count = 5;
				$newstr = '';
				for ($i = 0, $c = strlen($str); $i < $c; $i++)
				{
					$ch = ord($str{$i});
					$count += (($ch >= 0x80) || ($ch < 0x20)) ? ($transferEncoding == CPR_MIME_BASE64_SHORT) ? 2 : 3 : 1;
					$newstr .= $str{$i};

					if ($count >= CPR_MIME_LINE_LENGTH_LIMIT - strlen($toCharset) - 7)
					{
						$outArray[] = $newstr;
						$count = 0;
						$newstr = '';
					}
				}

				if (strlen($newstr) > 0)
				{
					$outArray[] = $newstr;
				}
			}
			return $outArray;
		}

		/**
		 * @param	string	$string
		 * @return	string
		 */
		function QuotedPrintableWithLineBreak($string, $dontBreake = false)
		{
			$linelen = 0;
			$breaklen = 0;
			$encodecrlf = false;
			$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
			$linebreak = ($dontBreake) ? '' : '='.CPR_CRLF;
			$len = strlen($string);
			$result = '';

			for($i = 0; $i < $len; $i++)
			{
				if ($linelen >= CPR_MIME_LINE_LENGTH_LIMIT)
				{
					$linelen = $breaklen;
					$result.= $linebreak;
				}
				$c = ord($string{$i});
				if (($c==0x3d) || ($c>=0x80) || ($c<0x20))
				{
					if ((($c==0x0A) || ($c==0x0D)) && (!$encodecrlf))
					{
						$result.= chr($c);
						$linelen = 0;
						continue;
					}
					/* $result .= '='.str_pad(strtoupper(dechex($c)), 2, '0'); */
					$result .= '='.$hex[floor($c/16)].$hex[floor($c%16)];
					$linelen += 3;
					continue;
				}
				$result.= chr($c);
				$linelen++;
			}

			return $result;
		}

		/**
		 * @param	string	$string
		 * @return	array
		 */
		function SearchEncodedPlaces($string)
		{
			$match = array('');
			preg_match_all('/=\?[^\?]+\?[Q|B]\?[^\?]*(\?=)/i', $string, $match);

			if (isset($match[0]))
			{
				for ($i = 0, $c = count($match[0]); $i < $c; $i++)
				{
					if (isset($match[0][$i]))
					{
						$pos = @strpos($match[0][$i], '*');
						if ($pos !== false)
						{
							$match[0][$i][0] = substr($match[0][$i][0], 0, $pos);
						}
					}
				}
				return $match[0];
			}
			
			return array();
		}

		/**
		 * @param	string	$str
		 * @return	string
		 */
		function WordExtensionsDecode($str)
		{
			$match = array('');
			preg_match_all('/([\w]+\*[^=]{0,3})=([^;\t\n]+)[;]?/', $str, $match);

			if (isset($match[0]) && count($match[0]) > 0)
			{
				$charArray = $newArray = array();
				for ($i = 0, $c = count($match[0]); $i < $c; $i++)
				{
					$temp = array('n' => '', 's' => '');

					$temp['n'] = substr($match[1][$i], 0, strpos($match[1][$i], '*'));
					
					$fpos = $spos = null;
					if ($match[1][$i]{strlen($match[1][$i])-1} == '*')
					{
						$fpos = strpos($match[2][$i], '\'');
						$spos = strpos($match[2][$i], '\'', $fpos + 1);
					}
					else
					{
						$fpos = false;
					}
					if ($fpos !== false)
					{
						$charset = substr($match[2][$i], 0, $fpos);
						$lang = substr($match[2][$i], $fpos + 1, $spos - $fpos - 1);
						if ($charset) $charArray[$temp['n']]['c'] = $charset;
						if ($lang) $charArray[$temp['n']]['l'] = $lang;

						if (isset($charArray[$temp['n']]['c']))
						{
							$temp['s'] = ConvertUtils::ConvertEncoding(urldecode(trim(substr($match[2][$i], $spos + 1), '\'"')), $charArray[$temp['n']]['c'], $GLOBALS[MailOutputCharset]);
						}
						else
						{
							$temp['s'] = urldecode(trim(substr($match[2][$i], $spos + 1), '\'"'));
						}
					}
					else
					{
						if (isset($charArray[$temp['n']]['c']))
						{
							$temp['s'] = ConvertUtils::ConvertEncoding(urldecode(trim($match[2][$i],'\'"')), $charArray[$temp['n']]['c'], $GLOBALS[MailOutputCharset]);
						}
						else
						{
							$temp['s'] = urldecode(trim($match[2][$i],'\'"'));
						}
					}
					$newArray[] = $temp;
				}

				for ($i = 0, $c = count($match[0]); $i < $c; $i++)
				{
					$str = str_replace($match[0][$i], '', $str);
				}

				$newMass = array();

				for ($i = 0, $c = count($newArray); $i < $c; $i++)
				{
					if (isset($newMass[$newArray[$i]['n']]))
					{
						$newMass[$newArray[$i]['n']] .= $newArray[$i]['s'];
					}
					else
					{
						$newMass[$newArray[$i]['n']] = $newArray[$i]['s'];
					}
				}

				if (count($newMass) > 0) $str = trim(trim($str), ';');
				foreach ($newMass as $k => $v)
				{
					$str .= '; '.$k.'="'.$v.'"';
				}
				return trim($str);
			}
			else
			{
				return $str;
			}
		}

		/**
		 * @param	string	$string
		 * @return	string
		 */
		public static function UuDecode($string)
		{
			$string = trim($string);
			if (strtolower(substr($string, 0, 5)) == 'begin')
			{
				$string = substr($string, strpos($string, CPR_CRLF) + strlen(CPR_CRLF));
				$string = substr($string, 0, strlen($string)-3);
				$string = trim($string);
			}

			if (function_exists('convert_uudecode'))
			{
				return convert_uudecode($string);
			}

			if (strlen($string) < 8)
			{
				return '';
			}

			$decoded = '';
			foreach (explode("\n", $string) as $line)
			{
				$c = count($bytes = unpack('c*', substr(trim($line), 1)));

				while ($c % 4)
				{
					$bytes[++$c] = 0;
				}

				foreach (array_chunk($bytes, 4) as $b)
				 {
					$b0 = $b[0] == 0x60 ? 0 : $b[0] - 0x20;
					$b1 = $b[1] == 0x60 ? 0 : $b[1] - 0x20;
					$b2 = $b[2] == 0x60 ? 0 : $b[2] - 0x20;
					$b3 = $b[3] == 0x60 ? 0 : $b[3] - 0x20;

					$b0 <<= 2;
					$b0 |= ($b1 >> 4) & 0x03;
					$b1 <<= 4;
					$b1 |= ($b2 >> 2) & 0x0F;
					$b2 <<= 6;
					$b2 |= $b3 & 0x3F;

					$decoded .= pack('c*', $b0, $b1, $b2);
				}
			}

			return rtrim($decoded, "\0");
		}

		/**
		 * @param	string	$_dateString
		 * @return	int
		 */
		public static function GetGmtTimeFromDateString($_dateString)
		{
			$dt = '';
			$matches = array();
			$datePattern = '/^(([a-z]*),[\s]*){0,1}(\d{1,2}).([a-z]*).(\d{2,4})[\s]*(\d{1,2}).(\d{1,2}).(\d{1,2})([\s]+([+-]?\d{1,4}))?([\s]*(\(?(\w+)\)?))?/i';

			if (preg_match($datePattern, $_dateString, $matches) && count($matches) > 7)
			{
				$year = $matches[5];
				$month = abs(self::GetMonthIndex(strtolower($matches[4])));
				$day = $matches[3];
				$hour = $matches[6];
				$minute = $matches[7];
				$second = $matches[8];

				$dt = mktime($hour, $minute, $second, $month, $day, $year);
			}

			return $dt;
		}

		/**
		 * @param	string	$_monthString
		 * @return	int
		 */
		public static function GetMonthIndex($_monthString)
		{
			switch (strtolower($_monthString))
			{
				default:	return -1;
				case 'jan':	return 1;
				case 'jan':	return 1;
				case 'feb':	return 2;
				case 'mar':	return 3;
				case 'apr':	return 4;
				case 'may':	return 5;
				case 'jun':	return 6;
				case 'jul':	return 7;
				case 'aug':	return 8;
				case 'sep':	return 9;
				case 'oct':	return 10;
				case 'nov':	return 11;
				case 'dec':	return 12;
			}
		}

		/**
		 * @param	int		$_timeStamp
		 * @return	string
		 */
		public static function ToANSI($_timeStamp)
		{
			return date('Y-m-d H:i:s', $_timeStamp);
		}
		
		/**
		 * $_ansiString should have YYYY-MM-DD HH:II:SS format
		 * @param	string		$_ansiString
		 * @return	int
		 */
		public static function GetGmtTimeFromANSIString($_ansiString)
		{
			$_dt = explode(' ', $_ansiString);
			$date = explode('-', $_dt[0]);
			$time = explode(':', $_dt[1]);

			return mktime($time[0], $time[1], $time[2], $date[1], $date[2], $date[0]);
		}
		
		/**
		 * @param	int		$codePageNum
		 * @return	string
		 */
		public static function GetCodePageName($codePageNum)
		{
			static $mapping = array(
						51936 => 'euc-cn',
						936 => 'gb2312',
						950 => 'big5',
						946 => 'euc-kr',
						50225 => 'iso-2022-kr',
						50220 => 'iso-2022-jp',
						932 => 'shift-jis',
						65000 => 'utf-7',
						65001 => 'utf-8',
						1250 => 'windows-1250',
						1251 => 'windows-1251',
						1252 => 'windows-1252',
						1253 => 'windows-1253',
						1254 => 'windows-1254',
						1255 => 'windows-1255',
						1256 => 'windows-1256',
						1257 => 'windows-1257',
						1258 => 'windows-1258',
						20866 => 'koi8-r',
						28591 => 'iso-8859-1',
						28592 => 'iso-8859-2',
						28593 => 'iso-8859-3',
						28594 => 'iso-8859-4',
						28595 => 'iso-8859-5',
						28596 => 'iso-8859-6',
						28597 => 'iso-8859-7',
						28598 => 'iso-8859-8');

			if (isset($mapping[$codePageNum]))
			{
				return $mapping[$codePageNum];
			}

			return 'utf-8';
		}

		/**
		 * @param	string	$codePageName
		 * @return	int
		 */
		public static function GetCodePageNumber($codePageName)
		{
			static $mapping = array(
						'euc-cn' => 51936,
						'gb2312' => 936,
						'big5' => 950,
						'euc-kr' => 949,
						'iso-2022-kr' => 50225,
						'iso-2022-jp' => 50220,
						'shift-jis' => 932,
						'utf-7' => 65000,
						'utf-8' => 65001,
						'windows-1250' => 1250,
						'windows-1251' => 1251,
						'windows-1252' => 1252,
						'windows-1253' => 1253,
						'windows-1254' => 1254,
						'windows-1255' => 1255,
						'windows-1256' => 1256,
						'windows-1257' => 1257,
						'windows-1258' => 1258,
						'koi8-r' => 20866,
						'iso-8859-1' => 28591,
						'iso-8859-2' => 28592,
						'iso-8859-3' => 28593,
						'iso-8859-4' => 28594,
						'iso-8859-5' => 28595,
						'iso-8859-6' => 28596,
						'iso-8859-7' => 28597,
						'iso-8859-8' => 28598);

			if (isset($mapping[$codePageName]))
			{
				return $mapping[$codePageName];
			}

			return 65001;
		}

	}